/*
 * spi_operate.c - CSP API of LomboTech SPI Controller
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/spi/spi.h>
#include <mach/csp.h>
#include "spi_operate.h"

void csp_spi_set_baud_rate(void *base, u32 clk_rate, u32 baud_rate)
{
	reg_spi_br_t temp;
	temp.val = 0;
	temp.bits.sckdv = 2; /* fixed to 2 */
	writel(temp.val, base + LOMBO_SPI_BR);
}

u32 csp_spi_get_baud_rate(void *base, u32 clk_rate)
{
	reg_spi_br_t temp;
	temp.val = readl(base + LOMBO_SPI_BR);
	return clk_rate / 2;
}

void csp_spi_en_cfg(void *base, u32 enable)
{
	reg_spi_en_t temp;
	temp.val = 0;
	temp.bits.ssi_en = enable;
	writel(temp.val, base + LOMBO_SPI_EN);
}

void csp_spi_xip_disable(void *base)
{
	reg_spi_ctl2_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL2);
	temp.bits.xip_dfs_fix = 0;
	temp.bits.xip_mod_bit_en = 0;
	writel(temp.val, base + LOMBO_SPI_CTL2);
}

void csp_spi_frame_format_cfg(void *base, u32 value)
{
	reg_spi_ctl0_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL0);
	if (value == FRAME_FORMAT_STANDARD_SPI)
		temp.bits.spi_frf = 0;
	else if (value == FRAME_FORMAT_DUAL_SPI)
		temp.bits.spi_frf = 1;
	else if (value == FRAME_FORMAT_QUAD_SPI)
		temp.bits.spi_frf = 2;
	else if (value == FRAME_FORMAT_OCTAL_SPI)
		temp.bits.spi_frf = 3;
	writel(temp.val, base + LOMBO_SPI_CTL0);
}

void csp_spi_trans_type_cfg(void *base, u32 value)
{
	reg_spi_ctl2_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL2);
	temp.bits.trans_type = value;
	writel(temp.val, base + LOMBO_SPI_CTL2);
}

void csp_spi_wait_cycle_cfg(void *base, u32 value)
{
	reg_spi_ctl2_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL2);
	temp.bits.wait_cycles = value;
	writel(temp.val, base + LOMBO_SPI_CTL2);
}

void csp_spi_all_pending_clear(void *base)
{
	writel(0xFFFFFFFF, base + LOMBO_SPI_INT_CLR);
}

void csp_spi_transfer_mode_cfg(void *base, u32 value)
{
	reg_spi_ctl0_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL0);
	temp.bits.tmod = value;
	writel(temp.val, base + LOMBO_SPI_CTL0);
}

void csp_spi_inst_len_cfg(void *base, u32 value)
{
	reg_spi_ctl2_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL2);
	if (value == 0)
		temp.bits.inst_l = 0x0;
	else if (value == 4)
		temp.bits.inst_l = 0x1;
	else if (value == 8)
		temp.bits.inst_l = 0x2;
	else if (value == 16)
		temp.bits.inst_l = 0x3;
	writel(temp.val, base + LOMBO_SPI_CTL2);
}

void csp_spi_addr_len_cfg(void *base, u32 value)
{
	reg_spi_ctl2_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL2);
	temp.bits.addr_l = value / 4;
	writel(temp.val, base + LOMBO_SPI_CTL2);
}

void csp_spi_dfs_cfg(void *base, u32 value)
{
	reg_spi_ctl0_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL0);
	temp.bits.dfs = value - 1;
	writel(temp.val, base + LOMBO_SPI_CTL0);
}

void csp_spi_ndf_cfg(void *base, u16 ndf_tx, u16 ndf_rx)
{
	reg_spi_ctl1_t temp;
	temp.val = 0;
	temp.bits.tx_ndf = ndf_tx;
	temp.bits.rx_ndf = ndf_rx;
	writel(temp.val, base + LOMBO_SPI_CTL1);
}

void csp_spi_inst_cfg(void *base, u32 value)
{
	writel(value, base + LOMBO_SPI_INST);
}

void csp_spi_addr0_cfg(void *base, u32 value)
{
	writel(value, base + LOMBO_SPI_ADDR0);
}

void csp_spi_addr1_cfg(void *base, u32 value)
{
	writel(value, base + LOMBO_SPI_ADDR1);
}

u32 csp_spi_is_rx_fifo_not_empty(void *base)
{
	reg_spi_status_t temp;
	temp.val = readl(base + LOMBO_SPI_STATUS);
	return temp.bits.rfne;
}

u32 csp_spi_read_data(void *base)
{
	return readl(base + LOMBO_SPI_DATA);
}

u32 csp_spi_is_tx_fifo_not_full(void *base)
{
	reg_spi_status_t temp;
	temp.val = readl(base + LOMBO_SPI_STATUS);
	return temp.bits.tfnf;
}

void csp_spi_write_data(void *base, u32 value)
{
	writel(value, base + LOMBO_SPI_DATA);
}

void csp_spi_cs_active_cfg(void *base, u32 cs_num, u32 active)
{
	reg_spi_sse_t temp;
	temp.val = readl(base + LOMBO_SPI_SSE);
	if (cs_num == 0)
		temp.bits.swss0o = active;
	else if (cs_num == 1)
		temp.bits.swss1o = active;
	else if (cs_num == 2)
		temp.bits.swss2o = active;
	else if (cs_num == 3)
		temp.bits.swss3o = active;
	writel(temp.val, base + LOMBO_SPI_SSE);
}

void csp_spi_cs_by_sw(void *base, u32 cs_num)
{
	reg_spi_sse_t temp;
	temp.val = readl(base + LOMBO_SPI_SSE);
	if (cs_num == 0)
		temp.bits.swss0oe = 1;
	else if (cs_num == 1)
		temp.bits.swss1oe = 1;
	else if (cs_num == 2)
		temp.bits.swss2oe = 1;
	else if (cs_num == 3)
		temp.bits.swss3oe = 1;
	writel(temp.val, base + LOMBO_SPI_SSE);
}

void csp_spi_cs_by_hw(void *base, u32 cs_num)
{
	reg_spi_sse_t temp;
	temp.val = readl(base + LOMBO_SPI_SSE);
	if (cs_num == 0)
		temp.bits.swss0oe = 0;
	else if (cs_num == 1)
		temp.bits.swss1oe = 0;
	else if (cs_num == 2)
		temp.bits.swss2oe = 0;
	else if (cs_num == 3)
		temp.bits.swss3oe = 0;
	writel(temp.val, base + LOMBO_SPI_SSE);
}

void csp_spi_cs_en_cfg(void *base, u32 cs_num, u32 enable)
{
	reg_spi_sse_t temp;
	temp.val = readl(base + LOMBO_SPI_SSE);
	if (cs_num == 0)
		temp.bits.sse0 = enable;
	else if (cs_num == 1)
		temp.bits.sse1 = enable;
	else if (cs_num == 2)
		temp.bits.sse2 = enable;
	else if (cs_num == 3)
		temp.bits.sse3 = enable;
	writel(temp.val, base + LOMBO_SPI_SSE);
}

void csp_spi_dma_tx_th_cfg(void *base, u32 value)
{
	reg_spi_dma_ctl_t temp;
	temp.val = readl(base + LOMBO_SPI_DMA_CTL);
	temp.bits.dmatxth = value;
	writel(temp.val, base + LOMBO_SPI_DMA_CTL);
}

void csp_spi_dma_rx_th_cfg(void *base, u32 value)
{
	reg_spi_dma_ctl_t temp;
	temp.val = readl(base + LOMBO_SPI_DMA_CTL);
	temp.bits.dmarxth = value - 1;
	writel(temp.val, base + LOMBO_SPI_DMA_CTL);
}

void csp_spi_dma_tx_en_cfg(void *base, u32 enable)
{
	reg_spi_dma_ctl_t temp;
	temp.val = readl(base + LOMBO_SPI_DMA_CTL);
	temp.bits.tdmae = enable;
	writel(temp.val, base + LOMBO_SPI_DMA_CTL);
}

void csp_spi_dma_rx_en_cfg(void *base, u32 enable)
{
	reg_spi_dma_ctl_t temp;
	temp.val = readl(base + LOMBO_SPI_DMA_CTL);
	temp.bits.rdmae = enable;
	writel(temp.val, base + LOMBO_SPI_DMA_CTL);
}

void csp_spi_mode_cfg(void *base, u32 mode)
{
	reg_spi_ctl0_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL0);
	if (mode == 0) {
		temp.bits.scpol = 0;
		temp.bits.scpha = 0;
	} else  if (mode == 1) {
		temp.bits.scpol = 0;
		temp.bits.scpha = 1;
	} else if (mode == 2) {
		temp.bits.scpol = 1;
		temp.bits.scpha = 0;
	} else if (mode == 3) {
		temp.bits.scpol = 1;
		temp.bits.scpha = 1;
	}
	writel(temp.val, base + LOMBO_SPI_CTL0);
}

void csp_spi_int_en_err_cfg(void *base, u32 enable)
{
	reg_spi_int_en_t temp;
	temp.val = readl(base + LOMBO_SPI_INT_EN);
	if (enable) {
		temp.bits.tx_fo = 1;
		temp.bits.rx_fo = 1;
		temp.bits.rx_fu = 1;
	} else {
		temp.bits.tx_fo = 0;
		temp.bits.rx_fo = 0;
		temp.bits.rx_fu = 0;
	}
	writel(temp.val, base + LOMBO_SPI_INT_EN);
}

u32 csp_spi_qry_irq_pending(void *base)
{
	return readl(base + LOMBO_SPI_INT_PD);
}

void csp_spi_clr_irq_pending(void *base, u32 pending)
{
	writel(pending, base + LOMBO_SPI_INT_PD);
}

u32 csp_spi_fsm_finish(void *base)
{
	reg_spi_int_pd_t temp;
	temp.val = readl(base + LOMBO_SPI_INT_PD);
	return temp.bits.fsm_finish;
}

u32 csp_spi_int_en_err_txfo(void *base, u32 pending)
{
	return (pending & LOMBO_SPI_INT_PD_TX_FO) ? 1 : 0;
}

u32 csp_spi_int_en_err_rxfu(void *base, u32 pending)
{
	return (pending & LOMBO_SPI_INT_PD_RX_FU) ? 1 : 0;
}

u32 csp_spi_int_en_err_rxfo(void *base, u32 pending)
{
	return (pending & LOMBO_SPI_INT_PD_RX_FO) ? 1 : 0;
}

u32 csp_spi_get_dma_src_addr(u32 base)
{
	return base + LOMBO_SPI_DATA;
}

u32 csp_spi_get_dma_dst_addr(u32 base)
{
	return base + LOMBO_SPI_DATA;
}

void csp_spi_rx_sample_delay_cfg(void *base, u32 baud)
{
	reg_spi_rx_sd_t temp;

	temp.val = readl(base + LOMBO_SPI_RX_SD);

	if (baud <= 24000000)
		temp.bits.rx_sd = RX_SD_25M;
	else if (baud <= 50000000)
		temp.bits.rx_sd = RX_SD_50M;
	else if (baud <= 75000000)
		temp.bits.rx_sd = RX_SD_75M;
	else if (baud <= 100000000)
		temp.bits.rx_sd = RX_SD_100M;

	writel(temp.val, base + LOMBO_SPI_RX_SD);
}

void csp_spi_rx_clk_delay_cfg(void *base, u32 baud)
{
}

void csp_spi_frame_format_sel(void *base, u32 value)
{
	reg_spi_ctl0_t temp;
	temp.val = readl(base + LOMBO_SPI_CTL0);
	temp.bits.frf = value;
	writel(temp.val, base + LOMBO_SPI_CTL0);
}

void csp_spi_set_little_endian(void *base)
{
	reg_spi_ftr_t temp;
	reg_spi_ctl0_t temp2;

	temp.val = readl(base + LOMBO_SPI_FTR);
	if (temp.bits.endian_ctl) {
		temp2.val = readl(base + LOMBO_SPI_CTL0);
		temp2.bits.endian_ctl = 1;
		writel(temp2.val, base + LOMBO_SPI_CTL0);
	}
}
